﻿using System;
using System.Collections;
using System.Collections.Generic;

using UnityEngine;
using Networking;

public class FPController : MonoBehaviour {

    public Transform playerCamera;

    CharacterController controller;

    public float moveSpeed = 5;

    Vector3 lastMouse;
    Vector3 mouseDelta;

    private bool sovereign = true;
    public bool dead = false;

    DelayedInterpolationManager movementManager;

    private EventQueue eventQueue = new EventQueue();

    private NetworkDungeonController gameController = null;

    #region Unity Events

    void Start()
    {
        if (playerCamera == null) playerCamera = transform.GetChild(0);
        controller = GetComponent<CharacterController>();
        lastMouse = Input.mousePosition;

        if (movementManager == null) movementManager = new DelayedInterpolationManager(this.transform);
        gameController = GameObject.FindGameObjectWithTag("GameController").GetComponent<NetworkDungeonController>();
    }

    void Update()
    {
        if (gameController.gameOver) return;
        if (dead)
        {
            this.GetComponent<MeshRenderer>().enabled = false;
        }
        if (sovereign)
        {
            HandleMovementControls();

            if (!dead)
            {
                HandleAttackControls();

                if (this.GetComponent<CombatData>().currentHealth <= 0)
                {
                    dead = true;
                    // ruh roh
                    if (gameController.singlePlayer || Network.isServer)
                    {
                        print("player died");
                    }
                    else
                    {
                        print("I seem to be dead - I should let everybody know");
                        PlayerDeathGameEvent deathEvent = new PlayerDeathGameEvent(Network.player);
                        eventQueue.AddEvent(deathEvent);
                    }
                }
            }
        }
        else
        {
            movementManager.DelayInterpolateMovement();
        }
    }

    void OnGUI()
    {
        //GUILayout.Label(mouseDelta.ToString());
    }

    void Awake()
    {
        if (!sovereign)
        {
            playerCamera.gameObject.SetActive(false);
        }
    }

    void OnNetworkInstantiate(NetworkMessageInfo info)
    {
        movementManager = new DelayedInterpolationManager(this.transform);

        GameObject.Find("GameController").GetComponent<NetworkDungeonController>().AddSceneObject(this.gameObject);
        print("Network instantiation, isMine is " + networkView.isMine);
        if (!networkView.isMine)
        {
            GameObject.Find("GameController").GetComponent<NetworkDungeonController>().gameRunning = true;
            sovereign = false;
            playerCamera.gameObject.SetActive(false);
        }
        DontDestroyOnLoad(this.gameObject);
    }

    void OnSerializeNetworkView(BitStream stream, NetworkMessageInfo info)
    {
        if (stream.isWriting)
        {
            movementManager.WriteSerializedData(stream);
            int eventCount = eventQueue.Count;
            stream.Serialize(ref eventCount);
            for (int i = 0; i < eventCount; i++)
            {
                // Player Attack
                eventQueue.GetNextEvent().SerializeForStream(stream);
            }
        }
        else
        {
            movementManager.ReadSerializedData(stream);

            int eventCount = 0;
            stream.Serialize(ref eventCount);

            for (int i = 0; i < eventCount; i++)
            {
                int readEventType = -1;
                stream.Serialize(ref readEventType);
                GameEvent.GameEventType readEventTypeValue = (GameEvent.GameEventType)readEventType;

                switch (readEventTypeValue)
                {
                    case GameEvent.GameEventType.PlayerAttack:
                        PlayerAttackGameEvent playerAttackEvent = new PlayerAttackGameEvent(stream);
                        // Kind of redundant to extract the attacking player; we already know who the attacking player is.
                        // It's this player.
                        if (Network.isServer)
                        {
                            ProcessPlayerAttack(playerAttackEvent);
                        }
                        break;
                    case GameEvent.GameEventType.PlayerDeath:

                        PlayerDeathGameEvent playerDeathEvent = new PlayerDeathGameEvent(stream);
                        NetworkDungeonController.GetNetworkPlayerObject(playerDeathEvent.Originator).GetComponent<FPController>().dead = true;
                        break;
                    default:
                        throw new ArgumentException("FPController - invalid game event of type " + readEventTypeValue.ToString() + " received, no handler defined");

                }

            }
        }
    }


    #endregion

    #region Input

    private void HandleAttackControls()
    {
        CombatData playerCombatData = this.GetComponent<CombatData>();
        if (playerCombatData.CanAttack())
        {
            if (Input.GetAxis("Fire1") == 1)
            {
                playerCombatData.Attack();
                PlayerAttackGameEvent attackEvent = new PlayerAttackGameEvent(Network.player, playerCombatData.attackPower);
                if (gameController.singlePlayer || Network.isServer)
                {
                    ProcessPlayerAttack(attackEvent);
                }
                else
                {
                    eventQueue.AddEvent(attackEvent);
                }
            }
        }
    }

    private void HandleMovementControls()
    {
        Vector3 moveDir = Vector3.zero;

        if (Input.GetKey(KeyCode.W)) moveDir = transform.forward;
        else if (Input.GetKey(KeyCode.S)) moveDir = transform.forward * -1;

        if (Input.GetKey(KeyCode.D)) moveDir += transform.right;
        else if (Input.GetKey(KeyCode.A)) moveDir -= transform.right;

        moveDir.Normalize();

        controller.SimpleMove(moveDir * (moveSpeed));

        float rotateX = Input.GetAxis("Mouse X") * 5;
        transform.Rotate(Vector3.up, rotateX);
    }

    #endregion

    void ProcessPlayerAttack(PlayerAttackGameEvent playerAttackEvent)
    {
        // Figure out which player this came from and process an attack
        GameObject[] skeletons = GameObject.FindGameObjectsWithTag("Skeleton");
        foreach (GameObject skeleton in skeletons)
        {
            Vector3 directionToEnemy = skeleton.transform.position - this.transform.position;
            if (Vector3.Angle(this.transform.forward, directionToEnemy) < this.GetComponent<CombatData>().sweep)
            {
                // SLAM!
                print("WHAM! skeleton is hit!");
                skeleton.GetComponent<SkeletonControl>().ReceiveHit(playerAttackEvent.damage);
            }
        }
    }

}
